
#include <iostream>
#include <map>
#include <tuple>

template <typename Result, typename... Args>
auto functional_memoization(Result (*f)(Args...))
{
    std::map<std::tuple<Args...>, Result> cache;
    // std::cout << "create functional memoization cache " << &cache << std::endl;
    return [f, cache](Args... args) mutable -> Result {
        // std::cout << "using cache at address = " << &cache << ", size: " << cache.size() << std::endl;
        // for (auto& item : cache) {
        //     std::apply([](auto&&... args) {
        //         ((std::cout << args << ";x "), ...); // fold expression
        //     },
        //         item.first);
        // }
        const auto args_tuple = std::make_tuple(args...);
        const auto cached = cache.find(args_tuple);
        if (cached == cache.end()) {
            auto result = f(args...);
            cache[args_tuple] = result;
            return result;
        } else {
            return cached->second;
        }
    };
}